﻿HmJS.$Import('core.core');

HmJS.register('lang.class', function ($) {

	var Class = this.Class;

	// #region -- Class.Mutators.Static --

	/**
	* @method: Class.Mutators.Static
	* @description: 对Mootools的extend方法的重定义，为类添加静态方法
	* @author: 苦苦的苦瓜
	* @date: 2011-08-21
	*/
	Class.Mutators.Static = function (items) {
		this.extend(items);
	};

	// #endregion 

	// #region -- Class.Mutators.TrackInstances from Forge --

	/*
	---
	version: 0.1-9
	description: Allows a class to track its instances by having instances array as a class property
	license: MIT-style license
	download: http://mootools.net/forge/p/mootools_trackinstances_class_mutator
	source: https://github.com/elado/mootools-trackinstances

	authors:
	- Elad Ossadon ( http://devign.me | http://twitter.com/devignblog )

	requires:
	- core/1.2.4:Class
	provides: [Class.Mutators.TrackInstances]
	...
	*/

	/*
	How to use
	----------

	Just add **TrackInstances:true** to your class definition, **after** the initialize method.


	var MyClass=new Class({
	initialize:function () {
	},

	TrackInstances:true,

	recalcPosition:function () {
	// something that recalculates position or any other task that should be applied on an instance
	}
	});

	var x=new MyClass();
	var y=new MyClass();

	MyClass.instances; // [x, y]
	MyClass.instances.length; // 2

	// from another code:
	window.addEvent("resize",function () {
	MyClass.instances.each(function (instance) {
	instance.recalcPosition();
	});
	});
	...
	*/

	Class.Mutators.TrackInstances = function (allow) {
		if (!allow) return;

		// save current initialize method
		var oldInit = this.prototype.initialize;
		var klass = this;

		// overwrite initialize method
		klass.prototype.initialize = function () {
			(klass.instances = klass.instances || []).push(this);
			oldInit.apply(this, arguments);
		};
	};

	// #endregion

	// #region -- Class.Mutators.Attributes from Forge --

	/**
	* version: 1.2-1
	* description: Adds attributes functionality to class instances with setters, getters, validators and change event listeners
	* license: MIT-style
	* download: http://mootools.net/forge/p/class_attributes
	* source: https://github.com/fantactuka/mootools-attributes/
	*
	* authors:
	*		- Maksim Horbachevsky
	*		- 苦苦的苦瓜
	*
	* requires:
	*		- /Class
	*		- /$util
	*
	* provides: [Class.Attributes]
	* 
	* 修改:
	*		! Class.Attributes.addAttribute方法, 如果添加的属性已经存在，之前是覆盖之前属性值，现在改为不覆盖，即什么也不做
	*		! Class.Attributes.set方法, 如果新旧属性值一样，则不触发事件
	**/

	/**
	* @module Class.Mutators.Attributes
	* @description allows to add attributes with setters, getters, validators and some more help methods
	* 每个属性都是一个对象，这个对象可以包含下面这些属性方法value, valueFn()，getter(), setter(), readOnly, validator()
	*
	* @method $setter, $getter
	* @description in case $setter or $getter (or both) is defined in attributes list as functions they will be used as
	* default setter and getter functions for non-existing attributes.
	*
	* That means if you didn't add `name` attribute in the Attributes list and tries to get or set it
	* .set('name', 'Bob'), .get('name') $setter and $getter will be invoked.
	*
	* If you DON'T have $setter or $getter defined and try to access non-existing attribute - getter will
	* return undefined, setter won't do anything at all.
	*
	* @example
	* var Employee = new Class({
	*     Attributes: {
	*         age: { }
	*     }
	* });
	*
	* var employee = new Employee();
	* employee.get('name'); // -> undefined
	* employee.set('name', 'bob'); // -> do nothing, does not throw error
	*
	* var Employee = new Class({
	*     Attributes: {
	*         $getter: function(name) {
	*             return this.getCookieData(name);
	*         },
	*         age: { }
	*     },
	*
	*     initialize: function(attributes) {
	*          ...
	*     },
	*
	*     getCookieData: function(name) {
	*         return Cookie.read(name);
	*     }
	* });
	*
	* var employee = new Employee();
	* employee.get('name'); // -> cookie 'name'
	*/

	Class.Mutators.Attributes = function (attributes) {

		var $setter = attributes.$setter,
				$getter = attributes.$getter;
		delete attributes.$setter;
		delete attributes.$getter;

		this.implement({

			/**
			* @property $attributes
			* @description storage for instance attributes
			*/
			$attributes: attributes,

			/**
			* @method get
			* @param name {String} - attribute name
			* @description attribute getter
			*/
			get: function (name) {
				var attr = this.$attributes[name];
				if (attr) {
					// valueFn()对属性的值进行初始化，不需要任何参数
					if (attr.valueFn && !attr.initialized) {
						attr.initialized = true;
						attr.value = attr.valueFn.call(this);
					}

					// 优先执行属性中定义的getter方法来获取属性的值
					if (attr.getter) {
						return attr.getter.call(this, attr.value);
					} else {
						return attr.value;
					}
				} else {
					// 如果get的属性没有定义，则通过$getter方法来取得属性值
					// $getter方法通过使用name的值if或switch扩展更多的属性，不过这些属性的readOnly,validator等方法需要自己在$getter中定义
					return $getter ? $getter.call(this, name) : undefined;
				}
			},

			/**
			* @method set
			* @param name {String} - attribute name
			* @param value {Object} - attribute value
			* @description attribute setter
			*/
			set: function (name, value) {
				var attr = this.$attributes[name];
				if (attr) {
					// 首先判断是不是只读属性
					if (!attr.readOnly) {
						// 先缓存旧的属性值
						var oldVal = attr.value, newVal;

						// 判断属性的校验函数存不存在，如果存在则通过校验函数确认可不可以赋值
						if (!attr.validator || attr.validator.call(this, value)) {
							// 优先使用属性中定义的setter方法为属性赋值
							if (attr.setter) {
								newVal = attr.setter.call(this, value);
							} else {
								newVal = value;
							}
							attr.value = newVal;

							// #region - Extended by 苦苦的苦瓜 -

							/**
							# 2011-08-14
							# 如果新旧属性值一样，则不触发事件
							**/
							if (oldVal !== value) {
								// 触发自定义事件
								this.fireEvent(name + 'Change', { newVal: newVal, oldVal: oldVal });
							}

							// #endregion
						}
					}
				} else if ($setter) {
					// 如果属性没有定义
					if ($setter) { $setter.call(this, name, value); }
				}
			},

			/**
			* @method setAttributes
			* @param attributes {Object} - a list of attributes to be set to the instance
			* @description set passed attributes passing it through .set method
			*/
			setAttributes: function (attributes) {
				Object.each(attributes, function (value, name) {
					this.set(name, value);
				}, this);
			},

			/**
			* @method getAttributes
			* @description returns a key-value object of all instance attributes
			* @returns {Object}
			*/
			getAttributes: function () {
				var attributes = Object.clone(this.$attributes);
				return attributes;
			},

			/**
			* @method addAttributes
			* @param attributes {Object} - a list of new attributes to be added to the instance
			* @description adds list of attributes to the instance
			*/
			addAttributes: function (attributes) {
				Object.each(attributes, function (value, name) {
					this.addAttribute(name, value);
				}, this);
			},

			/**
			* @method addAttribute
			* @param name {String} - new attribute name
			* @param value {Object} - new attribute value
			* @description adds new attribute to the instance
			*/
			// 这里的value属性是一个对象，可以包含下面这些属性方法value, valueFn()，getter(), setter(), readOnly, validator()
			addAttribute: function (name, value) {
				var attr = this.$attributes[name];

				// #region - Extended by 苦苦的苦瓜 -

				/**
				# 2011-08-14
				# 如果属性已经存在则不覆盖前属性
				**/
				if (!attr) {
					attr = value;
				}

				// #endregion

				return this;
			}

		});

	};

	// #endregion Class.Attributes

	// #region -- interfaceOf --

	/**
	* @method interfaceOf
	* @param item {Class} - Class实例对象
	* @param IObject {Interface} - Interface对象
	* @description 检查一个Class实例对象是否是一个特定接口的实例
	* @author 苦苦的苦瓜
	* @date 2011-08-21
	*/
	this.interfaceOf = function (item, IObject) {
		if (item == null) { return false; }
		if (typeOf(IObject) === 'interface' && item.Interfaces && item.Interfaces.indexOf(IObject) !== -1) {
			return true;
		} else {
			return false;
		}
	};

	// #endregion

	// #region -- Class.Mutators.Interfaces --

	/**
	---
	* version: 1.3-1
	* description: Interfaces provides some Interface functionality to Class (and also provides an Interface Object)
	* license: MIT-style
	* download: http://mootools.net/forge/p/interfaces
	* source: http://github.com/sebwoh/Mootools-Class-Interfaces
	*
	* authors:
	*		- Sebastian Wohlrab
	*		- 苦苦的苦瓜
	*
	* requires: [core.class]
	*
	* provides: [Class.Interfaces]
	*
	* 修改:
	*		! 修改Interface为Type类型，可以使用typeOf辨别Interface实例的类型为'interface'
	*		! 修改了Class.Interfaces接口中定义的成员与类中定义的成员类型一直的检测，成员函数参数个数的检测，
	*			※此处修改参照cpojer-mootools-interface-1.0.5-0-gc90cf8e※
	...
	**/

	this.Interface = new Type('Interface', function (name, members) {

		members.Interface = {
			Name: name,
			Check: function (obj) {
				for (p in members) {
					if (p.charAt(0) !== '$') { // Skip Internal
						switch (p) {
							case "Interface": /* reservated 保留关键字*/
								break;

							case "Interfaces":
								/**
								# 确保在接口中引用的子接口在主类的Interfaces中也要引用
								**/
								var existing = false,
										iName = '';
								Array.from(members[p]).each(function (iNeeded) {
									Array.from(obj[p]).each(function (iExisting) {
										if (iNeeded.Interface.Name === iExisting.Interface.Name) {
											existing = true;
										}
									});
									if (!existing) {
										iName = iNeeded.Interface.Name;
									}
								});
								if (!existing) {
									throw new Error('[' + this.Name + '] 中的子接口 [' + iName + '] 没有在主类的"' + p + '"中定义');
								}
								break;

							default:
								if (!(p in obj)) {
									throw new Error('[' + this.Name + '] The following Interface members are not implemented yet: "' + p + '"');
								}

								var item = obj[p],
										object = members[p];

								if (object != null) {
									var type = typeOf(item),
											oType = typeOf(object);

									// Needs to be same datatype OR instance of the provided object
									if (type !== oType && !instanceOf(item, object)) {
										var proto = object.prototype,
												name = (proto && proto.$family) ? proto.$family().capitalize() : object.displayName;
										throw new Error('[' + this.Name + '] Property "' + p + '" is implemented but not an instance of ' + (name ? '"' + name + '"' : 'the expected type'));
									}

									if ((oType === 'function') && (item.$origin.length < object.length)) {
										throw new Error('[' + this.Name + '] Property "' + p + '" does not implement at least ' + object.length + ' parameter' + (object.length != 1 ? 's' : ''));
									}
								}
								break;
						}
					}
				}
			}
		};

		//return members;
		return Object.append(members, this);

	});

	Class.Mutators.Interfaces = function (interfaces) {
		if (!this.prototype.initialize) {
			this.implement('initialize', function () { });
		}
		return interfaces;
	};

	// #endregion

	// #region -- Class.Mutators.Binds from More --

	/**
	* name: Class.Binds
	* 
	* description: Automagically binds specified methods in a class to the instance of the class.
	* 
	* authors:
	*		- Aaron Newton
	* 
	* requires:
	*		- Core/Class
	*		- /MooTools.More
	* 
	* provides: [Class.Binds]
	**/

	// 如果事先载入了more.class.binds，则不做声明
	if (!Class.Mutators.Binds) {
		Class.Mutators.Binds = function (binds) {
			if (!this.prototype.initialize) {
				this.implement('initialize', function () { });
			}
			return Array.from(binds).concat(this.prototype.Binds || []);
		};
	}

	// #endregion Class.Binds

	// #region -- Class.Mutators.initialize --

	// 这里要覆盖more.class.binds中的Class.Mutators.initialize
	Class.Mutators.initialize = function (initialize) {
		return function () {

			// #region - for Class.Binds -

			Array.from(this.Binds).each(function (name) {
				var original = this[name];
				if (original) {
					this[name] = original.bind(this);
				}
			}, this);

			// #endregion

			// #region - for Class.Interfaces -

			Array.from(this.Interfaces).each(function (implemented) {
				implemented.Interface.Check(this);
			}, this);

			// #endregion

			return initialize.apply(this, arguments);
		};
	};

	// #endregion

});
